// 错误防抖
function badDebounce(fn, delay) {
  let timer = null;
  // ❌ 返回箭头函数，this 无法动态绑定
  return (...args) => {
    if (timer) {
      clearTimeout(timer);
    }
    timer = setTimeout(() => {
      fn(...args); // this 指向错误！
    }, delay);
  };
}
// 基础版防抖 首次不立即执行
function baseDebounce(fn, delay) {
  let timer = null;
  return function (...args) {
    const context = this;
    if (timer) {
      clearTimeout(timer);
    }
    timer = setTimeout(() => {
      fn.call(context, ...args);
    }, delay);
  };
}
// 基础版节流 首次立即执行
function baseThrottle(fn, delay) {
  let timer = null;
  return function (...args) {
    const context = this;
    if (!timer) {
      fn.call(context, ...args);
      timer = setTimeout(() => {
        timer = null;
      }, delay);
    }
  };
}
// 防抖 可配置首次是否立即执行
function jzDebounce(fn, delay, leading = false) {
  let timer = null;
  let isCooling = false; // 冷却状态标记

  return function (...args) {
    const context = this;
    // 首次立即执行模式
    if (leading && !timer && !isCooling) {
      fn.apply(context, args); // ✅ 首次触发立即执行
      isCooling = true;
      timer = setTimeout(() => {
        isCooling = false;
        timer = null;
      }, delay);
    }
    // 基础模式（延迟执行）
    else {
      clearTimeout(timer);
      timer = setTimeout(() => {
        if (!leading) {
          // 非首次模式才执行
          fn.apply(context, args);
        }
        timer = null;
      }, delay);
    }
  };
}
// 节流 可配置首次是否立即执行
function jzThrottle(fn, delay, leading = true) {
  let timer = null;
  return function (...args) {
    const context = this;
    if (leading && !timer) {
      fn.call(context, ...args);
      timer = setTimeout(() => {
        timer = null;
      }, delay);
    } else if (!timer) {
      timer = setTimeout(() => {
        fn.call(context, ...args);
        timer = null;
      }, delay);
    }
  };
}

// 手写 深拷贝
const deepClone = target => {
  if (typeof target !== 'object' || target === null) {
    return target;
  }
  let res;
  if (Array.isArray(target)) {
    res = [];
    for (const i of target) {
      res.push(deepClone(i));
    }
  } else if (target.constructor === RegExp || target.constructor === Date) {
    res = target;
  } else {
    res = {};
    for (const key of target) {
      if (target.hasOwnProperty(key)) {
        res[key] = deepClone(target[key]);
      }
    }
  }
  return res;
};

// 测试用例
const obj = {
  name: 'Alice',
  logName: function (a, b) {
    console.log('参数', a, b);
    console.log(this.name); // 输出 "Alice"
  },
  logNameBadDebounce: badDebounce(function () {
    console.log(this.name); // 期望输出 "Alice", 但实际输出 undefined
  }, 1000),
  logNameDebounce: jzDebounce(function () {
    console.log(this.name); // 输出 "Alice"
  }, 1000)
};
const obj2 = {
  name: 'obj2'
};
// obj.logName();
// obj.logNameBadDebounce();
// obj.logNameDebounce();

// 解决this指向(把当前this函数赋值给传入的对象的属性)，再调用该对象属性（传参），返回执行结果
Function.prototype.mycall = function (context, ...args) {
  // console.log(context, args, this);
  // this(...args); //this代表函数本身，这样写只是调用函数，没修改this的值
  context = context == null ? window : Object(context);
  const fnSymbol = Symbol();
  context[fnSymbol] = this;
  const result = context[fnSymbol](...args);
  delete context[fnSymbol];
  return result;
};
Function.prototype.myapply = function (context, args) {
  console.log(context, args, this);
  context = context == null ? window : Object(context);
  // ✅ 参数校验（简单版）
  args = Array.isArray(args) ? args : [];
  const fnSymbol = Symbol();
  context[fnSymbol] = this;
  const result = context[fnSymbol](...args);
  delete context[fnSymbol];
  return result;
};
Function.prototype.mybind = function (context, args) {
  return (...args2) => {
    return this.mycall(context, ...args, ...args2);
  };
};
// obj.logName.apply(obj2, [1, 2]);
obj.logName.mycall(obj2, 1, 2);
// obj.logName.myapply(obj2);

// 手写new  constructor:构造函数 args：形参列表
function myNew(constructor, ...args) {
  //1.基于构造函数的原型对象创建实例对象
  const instance = Object.create(constructor.prototype);
  //2.执行构造函数，指定this指向，传参
  const res = constructor.apply(instance, args);
  //3.看看执行返回值是否是对象，如果是则返回该值；否则返回该实例对象
  if (res instanceof Object) {
    return res;
  }
  return instance;
}

// instanceof 运算符用于检查一个对象是否是另一个对象的实例
function myInstanceof(obj, constructor) {
  // a instanceof b
  // 基本类型直接返回false
  if (typeof obj !== 'object' || constructor === null) return false;
  let proto = Object.getPrototypeOf(obj); // 获取对象的原型对象
  while (proto) {
    if (proto === constructor.prototype) {
      return true; // 找到匹配的原型对象，返回true
    }
    proto = Object.getPrototypeOf(proto); // 继续向上查找原型
  }
  return false; // 没有找到匹配的原型对象，返回false
}

/**
 * 为指定元素添加事件监听器
 * 该函数旨在提供一个跨浏览器的解决方案，用于添加事件监听器到指定的DOM元素上
 * 它通过检查元素是否具有addEventListener或attachEvent方法来确定使用哪种方式添加事件监听器
 * 如果两种方法都没有，它将直接将事件处理程序赋值给元素的对应事件属性
 * @param {HTMLElement} element - 需要添加事件监听器的DOM元素
 * @param {string} eventName - 要监听的事件名称，如'click'、'mouseover'等
 * @param {Function} handler - 当事件触发时将被调用的函数
 */
function addEvent(element, eventName, handler) {
  if (element.addEventListener) {
    element.addEventListener(eventName, handler, false);
  } else if (element.attachEvent) {
    element.attachEvent('on' + eventName, handler);
  } else {
    element['on' + eventName] = handler;
  }
}
// 改造上述函数，使其成为惰性函数（执行一次后改写函数，后续调用直接执行）
function dxAddEvent(element, eventName, handler) {
  if (element.addEventListener) {
    dxAddEvent = function (element, eventName, handler) {
      element.addEventListener(eventName, handler, false);
    };
  } else if (element.attachEvent) {
    dxAddEvent = function (element, eventName, handler) {
      element.attachEvent('on' + eventName, handler);
    };
  } else {
    dxAddEvent = function (element, eventName, handler) {
      element['on' + eventName] = handler;
    };
  }
  return dxAddEvent(element, eventName, handler);
}

function curry(fn) {
  return function curried(...args) {
    // 如果传入参数个数大于要柯里化的函数形参个数
    if (args.length >= fn.length) {
      return fn.apply(this, args);
    } else {
      return (...args2) => curried.apply(this, [...args, ...args2]);
    }
  };
}
const fn = function (a, b, c) {
  // console.log('curry-this', this);
  return a + b + c;
};
const add = curry(fn);
add(1, 2)(3);
const curryObj = {
  name: 'Alice',
  add: curry(fn)
};
curryObj.add(1, 2)(3); // curryObj调用add，
